home *** CD-ROM | disk | FTP | other *** search
/ Graphics Plus / Graphics Plus.iso / msdos / raytrace / pov / gen / frgen13 / frgen.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1992-11-29  |  16.2 KB  |  660 lines

  1. //-------------------------------------------------------------------------
  2. //
  3. //                  Fractal Landscape Generator v1.3
  4. //                   Copyright (c) 1992 Steve Anger
  5. //                This program is freely distributable
  6. //
  7. //   FRGEN is a utility to generate fractal landscapes and shapes using
  8. // successive triangle sub-division. The fractal data can be output as
  9. // POV-Ray 1.0 or Vivid 2.0 scene description files or as raw triangle
  10. // data.
  11. //
  12. // Notes for compiling under Turbo/Borland C++: Compile with COMPACT or
  13. // LARGE memory model with default optimizations.
  14. //
  15. //                                      CompuServe: 70714,3113
  16. //                                       YCCMR BBS: (708)358-5611
  17. //
  18. //-------------------------------------------------------------------------
  19.  
  20. #include <fstream.h>
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include <ctype.h>
  25. #include <math.h>
  26. #include <values.h>
  27. #include <time.h>
  28. #include "vector.hpp"
  29.  
  30. #ifdef __TURBOC__
  31. #include <graphics.h>
  32. #include <conio.h>
  33. #include <dos.h>
  34. extern unsigned _stklen = 16000;  // Larger stack size for recursion
  35. #endif
  36.  
  37. typedef enum bool {false = 0, true = 1};
  38. typedef enum Format {POV, VIVID, RAW};
  39.  
  40. const char ver[] = "v1.3";
  41.  
  42. // Function prototypes
  43. void usage (void);
  44. void abortmsg (char *msg, int exit_code);
  45. void add_ext (char *fname, char *ext, bool force);
  46. void swap (float &a, float &b);
  47. void process_args (int argc, char *argv[]);
  48. void file_args (void);
  49. void process_option (char *s);
  50. void write_options (void);
  51. bool read_triangle (Vector&a, Vector&b, Vector&c, int &fix_ab, int &fix_bc,
  52.      int &fix_ca);
  53. Vector noise_vector (Vector&v);
  54. void tri_fractal (Vector&a, Vector&b, Vector&c, int fix_ab, int fix_bc,
  55.      int fix_ca, int level, Vector&vmin, Vector&vmax);
  56. void set_view (void);
  57. char upcase (char c);
  58. void plot_tri (const Vector&a, const Vector&b, const Vector&c);
  59. int init_display (void);
  60. void close_display (void);
  61. void check_abort (void);
  62.  
  63.  
  64. // Global variables
  65. fstream f, g;
  66. Vector  viewpoint;      // Where you're looking from
  67. Vector  lookat;         // Where you're looking at
  68. Vector  nx, ny, nz;     // Basis vectors for viewpoint
  69. Vector  nscale;         // Noise scaling factors
  70. Vector  nbias;          // Noise biasing factors
  71. int     depth;          // Depth of recursion
  72. int     seed;           // Seed for random number generator
  73. bool    display;        // Set to true if display preview enabled
  74. bool    swap_yz;        // Swap y and z coords on output
  75. Format  format;         // Output file format
  76. char    infile[64];     // Input file name
  77. char    outfile[64];    // Output file name
  78. long    tri_count;      // Count of triangles generated
  79. int     input_line;     // Current input line being parsed
  80. Vector  gmin, gmax;
  81.  
  82.  
  83. int main (int argc, char* argv[])
  84. {
  85.     cerr << "\nFractal Landscape Generator " << ver << " ";
  86.     cerr << "Copyright (c) 1992 Steve Anger\n";
  87.     cerr << "This program is freely distributable\n\n";
  88.  
  89.     srand ((unsigned)time(NULL));
  90.     seed = (int)(10000.0 * rand() / (float)RAND_MAX);
  91.     infile[0] = '\0';
  92.     outfile[0] = '\0';
  93.     viewpoint = Vector (10.0, 10.0, -10.0);
  94.     lookat = Vector (0.0, 0.0, 0.0);
  95.     nscale = Vector (0.10, 0.10, 0.10);
  96.     nbias = Vector (0.0, 0.0, 0.0);
  97.     depth = 3;
  98.     display = false;
  99.     swap_yz = false;
  100.     format = POV;
  101.     tri_count = 0;
  102.     input_line = 0;
  103.  
  104.     for (int i = 1; i < argc; i++) {
  105.     if (argv[i][0] != '-' && argv[i][0] != '/') {
  106.         if (infile[0] == '\0') {
  107.         strcpy (infile, argv[i]);
  108.         add_ext (infile, "fr", false);
  109.         }
  110.         else if (outfile[0] == '\0') {
  111.         strcpy (outfile, argv[i]);
  112.  
  113.         }
  114.             else
  115.                 abortmsg ("Too many file names specified.", 1);
  116.         }
  117.     }
  118.  
  119.     if (strlen(infile) == 0 || strlen(outfile) == 0) {
  120.     usage();
  121.         exit (1);
  122.     }
  123.  
  124.     f.open (infile, ios::in);
  125.     if (!f) abortmsg ("Error opening input file.", 1);
  126.  
  127.     file_args();  // Get options from input file
  128.  
  129.     process_args (argc, argv);  // Get the command line options
  130.  
  131.     switch (format) {
  132.     case POV:   add_ext (outfile, "pov", false);
  133.             break;
  134.     case VIVID: add_ext (outfile, "vo", false);
  135.                     break;
  136.         case RAW:   add_ext (outfile, "raw", false);
  137.             break;
  138.     }
  139.  
  140.     g.open (outfile, ios::out);
  141.     if (!g) abortmsg ("Error opening output file.", 1);
  142.  
  143.     if (depth < 1)
  144.         abortmsg ("Recurse depth (-r) must be >= 1", 1);
  145.  
  146.     cout << "Generating fractal\n";
  147.  
  148.     if (display) {
  149.         set_view();
  150.  
  151.         if (init_display() < 0)
  152.             abortmsg ("Unable to initialize graphics display.", 1);
  153.     }
  154.  
  155.     write_options();
  156.  
  157.     if (format == POV) {
  158.     g << "#declare FracTexture = texture {\n";
  159.     g << "    color red 1.0 green 1.0 blue 1.0\n";
  160.     g << "    ambient 0.1\n";
  161.     g << "    diffuse 0.7\n";
  162.     g << "}\n\n";
  163.  
  164.     g << "composite {\n";
  165.     }
  166.  
  167.     Vector a, b, c, vmin, vmax;
  168.     int fix_ab, fix_bc, fix_ca, tri_read = 0;
  169.  
  170.     Vector gmin = Vector (+MAXFLOAT, +MAXFLOAT, +MAXFLOAT);
  171.     Vector gmax = Vector (-MAXFLOAT, -MAXFLOAT, -MAXFLOAT);
  172.  
  173.     while (read_triangle (a, b, c, fix_ab, fix_bc, fix_ca)) {
  174.     tri_fractal (a, b, c, fix_ab, fix_bc, fix_ca, depth, vmin, vmax);
  175.  
  176.     gmin = min (gmin, vmin);
  177.     gmax = max (gmax, vmax);
  178.     tri_read++;
  179.     }
  180.  
  181.     // Add a bounding box to the entire fractal
  182.     if (format == POV && tri_read > 1) {
  183.     g << "\tbounded_by { box { <" << gmin << "> <" << gmax << "> } }\n";
  184.     g << "}\n\n";
  185.     }
  186.  
  187.     f.close();
  188.     g.close();
  189.  
  190.     cerr << "\n" << tri_count << " triangles generated.\n";
  191.  
  192.     if (display) {
  193.         cerr << "Press return.\n";
  194.         getchar();
  195.     close_display();
  196.     }
  197.  
  198.     return 0;
  199. }
  200.  
  201.  
  202. void usage()
  203. {
  204.     cerr << "Usage: frgen infile[.fr] outfile[.dat] [options]\n";
  205.     cerr << "Options: -sxnnn   Scale noise in x direction by nnn (0.0 - 1.0)\n";
  206.     cerr << "         -synnn     '     '    ' y     '      '  '     '     ' \n";
  207.     cerr << "         -sznnn     '     '    ' z     '      '  '     '     ' \n";
  208.     cerr << "         -bxnnn   Bias noise in  x direction by nnn (-1.0 - 1.0)\n";
  209.     cerr << "         -bynnn     '    '    '  y     '      '  '     '     '  \n";
  210.     cerr << "         -bznnn     '    '    '  z     '      '  '     '     '  \n";
  211.     cerr << "         -vxnnn   Set viewpoint x coord to nnn\n";
  212.     cerr << "         -vynnn    '      '     y   '    '  ' \n";
  213.     cerr << "         -vznnn    '      '     z   '    '  ' \n";
  214.     cerr << "         -lxnnn   Set lookat x coord to nnn\n";
  215.     cerr << "         -lynnn    '     '   y   '    '  ' \n";
  216.     cerr << "         -lznnn    '     '   z   '    '  ' \n";
  217.     cerr << "         -ennn    Use nnn as seed for random number generator\n";
  218.     cerr << "         -rnnn    Maximum recursion depth of nnn\n";
  219.     cerr << "         -d       Display generated fractal on screen\n";
  220.     cerr << "         -o[n]    Set output format (-op = PoV, -ov = Vivid, -or = raw)\n";
  221.     cerr << "         -x       Exchange Y and Z coords on output.\n";
  222. }
  223.  
  224.  
  225. void abortmsg (char *msg, int exit_code)
  226. {
  227.     if (display)
  228.         close_display();
  229.  
  230.     cerr << msg << "\n";
  231.     exit (exit_code);
  232. }
  233.  
  234.  
  235. void add_ext (char *fname, char *ext, bool force)
  236. {
  237.     int i;
  238.  
  239.     for (i = 0; i < strlen(fname); i++)
  240.         if (fname[i] == '.') break;
  241.  
  242.     if (fname[i] == '\0' || force) {
  243.         fname[i] = '.';
  244.     strcpy (&fname[i+1], ext);
  245.     }
  246. }
  247.  
  248.  
  249. void swap (float &a, float &b)
  250. {
  251.     float temp = a;
  252.     a = b;
  253.     b = temp;
  254. }
  255.  
  256.  
  257. void process_args (int argc, char* argv[])
  258. {
  259.     for (int i = 1; i < argc; i++) {
  260.         if (argv[i][0] == '-' || argv[i][0] == '/')
  261.         process_option (&argv[i][1]);
  262.     }
  263. }
  264.  
  265.  
  266. void file_args()
  267. {
  268.     char line[100] = "";
  269.     char last_ch = ' ';
  270.  
  271.     do {
  272.         f.getline (line, 100);
  273.         input_line++;
  274.     } while (!f.eof() && line[0] == ';');
  275.  
  276.     if (f.eof())
  277.         return;
  278.  
  279.     for (int i = 0; i < strlen (line); i++) {
  280.     if ((line[i] == '-' || line[i] == '/') && isspace(last_ch))
  281.         process_option (&line[i+1]);
  282.  
  283.         last_ch = line[i];
  284.     }
  285. }
  286.  
  287.  
  288. void process_option (char *s)
  289. {
  290.     switch (upcase(s[0])) {
  291.     case 'V':
  292.         switch (upcase(s[1])) {
  293.         case 'X': sscanf (&s[2], "%f", &viewpoint.x);
  294.               break;
  295.         case 'Y': sscanf (&s[2], "%f", &viewpoint.y);
  296.               break;
  297.         case 'Z': sscanf (&s[2], "%f", &viewpoint.z);
  298.               break;
  299.         default : cerr << "Invalid option -v" << s[1] << "\n";
  300.         }
  301.         break;
  302.  
  303.     case 'L':
  304.         switch (upcase(s[1])) {
  305.         case 'X': sscanf (&s[2], "%f", &lookat.x);
  306.               break;
  307.         case 'Y': sscanf (&s[2], "%f", &lookat.y);
  308.               break;
  309.         case 'Z': sscanf (&s[2], "%f", &lookat.z);
  310.               break;
  311.         default : cerr << "Invalid option -l" << s[1] << "\n";
  312.         }
  313.         break;
  314.  
  315.     case 'S':
  316.         switch (upcase(s[1])) {
  317.         case 'X': sscanf (&s[2], "%f", &nscale.x);
  318.               break;
  319.         case 'Y': sscanf (&s[2], "%f", &nscale.y);
  320.               break;
  321.         case 'Z': sscanf (&s[2], "%f", &nscale.z);
  322.               break;
  323.         default : cerr << "Invalid option -s" << s[1] << "\n";
  324.         }
  325.         break;
  326.  
  327.     case 'B':
  328.         switch (upcase(s[1])) {
  329.         case 'X': sscanf (&s[2], "%f", &nbias.x);
  330.               break;
  331.         case 'Y': sscanf (&s[2], "%f", &nbias.y);
  332.               break;
  333.         case 'Z': sscanf (&s[2], "%f", &nbias.z);
  334.               break;
  335.         default : cerr << "Invalid option -b" << s[1] << ", ignored\n";
  336.         }
  337.         break;
  338.  
  339.     case 'E': sscanf (&s[1], "%d", &seed);
  340.           break;
  341.  
  342.     case 'R': sscanf (&s[1], "%d", &depth);
  343.           break;
  344.  
  345.     case 'D': display = true;
  346.           break;
  347.  
  348.     case 'X': swap_yz = true;
  349.           break;
  350.  
  351.     case 'O' :
  352.         switch (upcase(s[1])) {
  353.         case 'P': format = POV;    break;
  354.         case 'V': format = VIVID;  break;
  355.         case 'R': format = RAW;    break;
  356.         default : cerr <<"Invalid option -o" << s[1] << ", ignored\n";
  357.         }
  358.         break;
  359.  
  360.  
  361.     default : cerr << "Invalid option -" << s[1] << ", ignored\n";
  362.     }
  363. }
  364.  
  365.  
  366. void write_options()
  367. {
  368.     if (format == POV || format == VIVID) {
  369.         g << "/*\n";
  370.         g << "     Generated with FRGEN " << ver << " from file " << infile << "\n";
  371.         g << "     Options in effect:";
  372.         g << " -sx" <<  nscale.x << " -sy" << nscale.y << " -sz" << nscale.z;
  373.         g << " -bx" << nbias.x << " -by" << nbias.y << " -bz" << nbias.z;
  374.         g << " -r" << depth << " -e" << seed << "\n";
  375.         g << "*/\n\n";
  376.     }
  377. }
  378.  
  379.  
  380. bool read_triangle (Vector &a, Vector &b, Vector &c,
  381.             int &fix_ab, int &fix_bc, int &fix_ca)
  382. {
  383.     char line[256] = "";
  384.     char msg[40];
  385.     int n;
  386.  
  387.     do {
  388.     f.getline (line, 256);
  389.     input_line++;
  390.     } while (!f.eof() && line[0] == ';');
  391.  
  392.     if (f.eof())
  393.     return false;
  394.  
  395.     a = Vector (0.0, 0.0, 0.0);
  396.     b = Vector (0.0, 0.0, 0.0);
  397.     c = Vector (0.0, 0.0, 0.0);
  398.  
  399.     fix_ab = 0;
  400.     fix_bc = 0;
  401.     fix_ca = 0;
  402.  
  403.     n = sscanf (line, "%f %f %f %f %f %f %f %f %f %d %d %d",
  404.         &a.x, &a.y, &a.z, &b.x, &b.y, &b.z, &c.x, &c.y, &c.z,
  405.         &fix_ab, &fix_bc, &fix_ca);
  406.  
  407.     if (n == EOF)
  408.     return false;
  409.  
  410.     if (!(n == 9 || n == 12)) {
  411.     sprintf (msg, "Error in input file, line %d", input_line);
  412.     abortmsg (msg, 1);
  413.     }
  414.  
  415.     return true;
  416. }
  417.  
  418.  
  419. Vector noise_vector (Vector &v)
  420. {
  421.     // Generate a random vector that is a function of the
  422.     // vector v's position
  423.     Vector noise;
  424.  
  425.     // seed the rand # generator with a mish-mash of the x, y, and z coords
  426.     srand (seed ^ (long)(-23465*v.x) ^ (long)(17365*v.y) ^ (long)(5364*v.z));
  427.  
  428.     noise.x = nscale.x * (2.0*rand()/RAND_MAX - 1.0 + nbias.x);
  429.     noise.y = nscale.y * (2.0*rand()/RAND_MAX - 1.0 + nbias.y);
  430.     noise.z = nscale.z * (2.0*rand()/RAND_MAX - 1.0 + nbias.z);
  431.  
  432.     return noise;
  433. }
  434.  
  435.  
  436. void tri_fractal (Vector &a, Vector &b, Vector &c,
  437.           int fix_ab, int fix_bc, int fix_ca, int level,
  438.           Vector &vmin, Vector &vmax)
  439. {
  440.     Vector ab, bc, ca;
  441.     float  ab_len, bc_len, ca_len;
  442.  
  443.     vmin = Vector (+MAXFLOAT, +MAXFLOAT, +MAXFLOAT);
  444.     vmax = Vector (-MAXFLOAT, -MAXFLOAT, -MAXFLOAT);
  445.  
  446.     if (level == 0) {
  447.     check_abort();
  448.  
  449.     if (swap_yz) {
  450.         swap (a.y, a.z);
  451.         swap (b.y, b.z);
  452.         swap (c.y, c.z);
  453.     }
  454.  
  455.     switch (format) {
  456.         case POV:
  457.         g << "\t\ttriangle { <" << a << "> <" << b << "> <" << c << "> }\n";
  458.         break;
  459.  
  460.         case VIVID:
  461.         g << "polygon {\n";
  462.         g << "\tpoints 3\n";
  463.         g << "\tvertex " << a << "\n";
  464.         g << "\tvertex " << b << "\n";
  465.         g << "\tvertex " << c << "\n";
  466.         g << "}\n\n";
  467.         break;
  468.  
  469.         case RAW:
  470.         g << a << " " << b << " " << c << "\n";
  471.         break;
  472.     }
  473.  
  474.     if (swap_yz) {
  475.         swap (a.y, a.z);
  476.         swap (b.y, b.z);
  477.         swap (c.y, c.z);
  478.     }
  479.  
  480.     tri_count++;
  481.  
  482.     if (display)
  483.         plot_tri (a, b, c);
  484.  
  485.     vmin = min (vmin, a);
  486.     vmin = min (vmin, b);
  487.     vmin = min (vmin, c);
  488.  
  489.     vmax = max (vmax, a);
  490.     vmax = max (vmax, b);
  491.     vmax = max (vmax, c);
  492.     }
  493.     else {
  494.     if (format == POV) {
  495.         if (level == 1) {
  496.         g << "\tobject {\n";
  497.         g << "\t\tunion {\n";
  498.         }
  499.         else if (level > 1)
  500.         g << "composite {\n";
  501.     }
  502.  
  503.     // Find the midpoints of the three line segments
  504.     ab = (a + b)/2.0;
  505.     bc = (b + c)/2.0;
  506.     ca = (c + a)/2.0;
  507.  
  508.     ab_len = mag(a - b);
  509.     bc_len = mag(b - c);
  510.     ca_len = mag(c - a);
  511.  
  512.     // Compute the normal to the triangle
  513.     Vector norm = (b - a) * (c - a);
  514.     norm = norm/mag(norm);
  515.  
  516.     // Create some noise proportional to the edge lengths
  517.     Vector noise_ab = ab_len * noise_vector (ab);
  518.     Vector noise_bc = bc_len * noise_vector (bc);
  519.     Vector noise_ca = ca_len * noise_vector (ca);
  520.  
  521.     // Don't let any 'fixed' points move out of the plane
  522.     // of the triangle (remove the noise's normal component)
  523.     if (fix_ab) noise_ab = noise_ab * norm;
  524.     if (fix_bc) noise_bc = noise_bc * norm;
  525.     if (fix_ca) noise_ca = noise_ca * norm;
  526.  
  527.     // Perturb the three vectors
  528.     ab = ab + noise_ab;
  529.     bc = bc + noise_bc;
  530.     ca = ca + noise_ca;
  531.  
  532.     Vector new_min[4], new_max[4];
  533.  
  534.     tri_fractal (a,  ab, ca, fix_ab, 0, fix_ca, level-1, new_min[0], new_max[0]);
  535.     tri_fractal (b,  bc, ab, fix_bc, 0, fix_ab, level-1, new_min[1], new_max[1]);
  536.     tri_fractal (c,  ca, bc, fix_ca, 0, fix_bc, level-1, new_min[2], new_max[2]);
  537.     tri_fractal (ab, bc, ca,      0, 0,      0, level-1, new_min[3], new_max[3]);
  538.  
  539.     for (int i = 0; i < 4; i++) {
  540.         vmin = min (vmin, new_min[i]);
  541.         vmax = max (vmax, new_max[i]);
  542.     }
  543.  
  544.     if (format == POV) {
  545.         if (level == 1) {
  546.         g << "\t\t}\n\n";
  547.  
  548.         g << "\t\ttexture { FracTexture }\n\n";
  549.         g << "\t\tbounded_by { box { <" << vmin << "> <" << vmax << "> } }\n";
  550.         g << "\t}\n\n";
  551.         }
  552.         else if (level > 1) {
  553.         g << "\tbounded_by { box { <" << vmin << "> <" << vmax << "> } }\n";
  554.         g << "}\n";
  555.         }
  556.     }
  557.     }
  558. }
  559.  
  560.  
  561. void set_view()
  562. {
  563.     Vector sky;
  564.  
  565.     sky = Vector (0.0, 1.0, 0.0);
  566.  
  567.     // Calculate a set of base vectors for the new viewpoint
  568.     nz = lookat - viewpoint;
  569.     nz = nz/mag(nz);
  570.  
  571.     ny = sky - nz*(nz.y/(nz % nz));
  572.     ny = ny/mag(ny);
  573.  
  574.     nx = 1.333 * nz * ny;
  575. }
  576.  
  577.  
  578. /* Convert character 'c' top upper case */
  579. char upcase (char c)
  580. {
  581.     if (c >= 'a' && c <= 'z')
  582.     c = c - 'a' + 'A';
  583.  
  584.     return c;
  585. }
  586.  
  587.  
  588. void plot_tri (const Vector &a, const Vector &b, const Vector &c)
  589. {
  590. #ifdef __TURBOC__
  591.     Vector pa, pb, pc, ta, tb, tc;
  592.     int ax, ay, bx, by, cx, cy;
  593.  
  594.     ta = a - viewpoint;
  595.     tb = b - viewpoint;
  596.     tc = c - viewpoint;
  597.  
  598.     pa = Vector (ta % nx, ta % ny, ta % nz);
  599.     pb = Vector (tb % nx, tb % ny, tb % nz);
  600.     pc = Vector (tc % nx, tc % ny, tc % nz);
  601.  
  602.     ax = int((0.5*pa.x/pa.z + 0.5)*getmaxx());
  603.     bx = int((0.5*pb.x/pb.z + 0.5)*getmaxx());
  604.     cx = int((0.5*pc.x/pc.z + 0.5)*getmaxx());
  605.     ay = int((0.5 - pa.y/pa.z)*getmaxy());
  606.     by = int((0.5 - pb.y/pb.z)*getmaxy());
  607.     cy = int((0.5 - pc.y/pc.z)*getmaxy());
  608.  
  609.     setcolor (LIGHTBLUE);
  610.     moveto (ax, ay);
  611.     lineto (bx, by);
  612.     lineto (cx, cy);
  613.     lineto (ax, ay);
  614. #endif
  615. }
  616.  
  617.  
  618. int init_display()
  619. {
  620. #ifdef __TURBOC__
  621.     int gdriver = DETECT, gmode, errorcode;
  622.  
  623.     initgraph (&gdriver, &gmode, "");
  624.     errorcode = graphresult();
  625.     if (errorcode != grOk) {
  626.         cerr << "ERROR:" << grapherrormsg (errorcode) << "\n\n";
  627.         return -1;
  628.     }
  629. #endif
  630.  
  631.     return 0;
  632. }
  633.  
  634.  
  635. void close_display()
  636. {
  637. #ifdef __TURBOC__
  638.     restorecrtmode();
  639. #endif
  640. }
  641.  
  642.  
  643. void check_abort()
  644. {
  645. #ifdef __TURBOC__
  646.     static int cnt = 0;
  647.  
  648.     ++cnt;
  649.  
  650.     if (cnt >= 10) {
  651.         cnt = 0;
  652.  
  653.         if (kbhit())
  654.             abortmsg ("Aborted!", 1);
  655.     }
  656. #endif
  657. }
  658.  
  659.  
  660.